/*
 * I2C.cpp
 *
 *  Created on: 21.11.2012
 *      Author: Michael
 */

#include "I2C.h"

#include "stm32f4xx_i2c.h"
#include "stm32f4xx.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_gpio.h"

#define FLAG_TIMEOUT         ((uint32_t)0x1000)
#define LONG_TIMEOUT         ((uint32_t)(10 * FLAG_TIMEOUT))

I2C::I2C(int ID) {
	this->ID = ID;

	switch (ID) {
	case I2C_ID1 :

		this->I2Cx = I2C1;

		cfg.I2C_CLK = RCC_APB1Periph_I2C1;

		cfg.I2C_SCL_PIN = GPIO_Pin_8;
		cfg.I2C_SCL_GPIO_PORT = GPIOB;
		cfg.I2C_SCL_GPIO_CLK = RCC_AHB1Periph_GPIOB;
		cfg.I2C_SCL_SOURCE = GPIO_PinSource8;

		cfg.I2C_SDA_PIN = GPIO_Pin_9;
		cfg.I2C_SDA_GPIO_PORT = GPIOB;
		cfg.I2C_SDA_GPIO_CLK = RCC_AHB1Periph_GPIOB;
		cfg.I2C_SDA_SOURCE = GPIO_PinSource9;

		cfg.I2C_AF = GPIO_AF_I2C1;
		cfg.I2C_SLAVE_ADDRESS7 = 0x00;

		cfg.I2C_SPEED = 30000;

		break;
	case I2C_ID2 :

		this->I2Cx = I2C2;

		cfg.I2C_CLK = RCC_APB1Periph_I2C2;

		cfg.I2C_SCL_PIN = GPIO_Pin_10;
		cfg.I2C_SCL_GPIO_PORT = GPIOB;
		cfg.I2C_SCL_GPIO_CLK = RCC_AHB1Periph_GPIOB;
		cfg.I2C_SCL_SOURCE = GPIO_PinSource10;

		cfg.I2C_SDA_PIN = GPIO_Pin_11;
		cfg.I2C_SDA_GPIO_PORT = GPIOB;
		cfg.I2C_SDA_GPIO_CLK = RCC_AHB1Periph_GPIOB;
		cfg.I2C_SDA_SOURCE = GPIO_PinSource11;

		cfg.I2C_AF = GPIO_AF_I2C2;
		cfg.I2C_SLAVE_ADDRESS7 = 0x00;

		cfg.I2C_SPEED = 100000;

		break;
	default:
		break;
	}

	init_I2C();
}

I2C::~I2C() {

}

void I2C::init_I2C() {

	I2C_InitTypeDef I2C_InitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;

	/*!< I2Cx Periph clock enable */
	RCC_APB1PeriphClockCmd(cfg.I2C_CLK, ENABLE);

	/*!< RCC_AHB1Periph_GPIOB enable */
	RCC_AHB1PeriphClockCmd(cfg.I2C_SCL_GPIO_CLK | cfg.I2C_SDA_GPIO_CLK, ENABLE);

	/* Reset I2Cx IP */
	RCC_APB1PeriphResetCmd(cfg.I2C_CLK, ENABLE);

	/* Release reset signal of I2C IP */
	RCC_APB1PeriphResetCmd(cfg.I2C_CLK, DISABLE);

	/*!< GPIO configuration */
	/* Connect PB10 to I2C_SCL*/
	GPIO_PinAFConfig(cfg.I2C_SCL_GPIO_PORT, cfg.I2C_SCL_SOURCE, cfg.I2C_AF);
	/* Connect PB11 to I2C_SDA*/
	GPIO_PinAFConfig(cfg.I2C_SDA_GPIO_PORT, cfg.I2C_SDA_SOURCE, cfg.I2C_AF);

	/*!< Configure I2Cx pins: SCL */
	GPIO_InitStructure.GPIO_Pin = cfg.I2C_SCL_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_Init(cfg.I2C_SCL_GPIO_PORT, &GPIO_InitStructure);

	/*!< Configure I2C pins: SDA */
	GPIO_InitStructure.GPIO_Pin = cfg.I2C_SDA_PIN;
	GPIO_Init(cfg.I2C_SDA_GPIO_PORT, &GPIO_InitStructure);

	/*!< I2C configuration */
	/* I2C configuration */
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
	I2C_InitStructure.I2C_OwnAddress1 = 0x00;
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
	I2C_InitStructure.I2C_AcknowledgedAddress = cfg.I2C_SLAVE_ADDRESS7;
	I2C_InitStructure.I2C_ClockSpeed = cfg.I2C_SPEED;

	/* I2Cx Peripheral Enable */
	I2C_Cmd(I2Cx, ENABLE);
	/* Apply I2Cx configuration after enabling it */
	I2C_Init(I2Cx, &I2C_InitStructure);

}

int I2C::write(uint8_t devAddr, uint8_t regAddr, uint8_t val) {

	uint32_t timeout;

	/*!< While the bus is busy */
	timeout = LONG_TIMEOUT;
	while (I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY )) {
		if ((timeout--) == 0)
			return 0xFF;
	}

	/*!< Send START condition */
	I2C_GenerateSTART(I2Cx, ENABLE);

	/*!< Test on EV5 and clear it */
	timeout = FLAG_TIMEOUT;
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT )) {
		if ((timeout--) == 0)
			return 0xFF;
	}

	/*!< Send device address for write */
	I2C_Send7bitAddress(I2Cx, devAddr, I2C_Direction_Transmitter );

	/*!< Test on EV6 and clear it */
	timeout = FLAG_TIMEOUT;
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED )) {
		if ((timeout--) == 0)
			return 0xFF;
	}

	/*!< Send the register address */
	I2C_SendData(I2Cx, regAddr);

	/*!< Test on EV8 and clear it */
	timeout = FLAG_TIMEOUT;
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING )) {
		if ((timeout--) == 0)
			return 0xFF;
	}

	/*!< Send value to slave */
	I2C_SendData(I2Cx, val);

	/*!< Test on EV8 and clear it */
	timeout = FLAG_TIMEOUT;
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING )) {
		if ((timeout--) == 0)
			return 0xFF;
	}

	/* Send I2C1 STOP Condition */
	I2C_GenerateSTOP(I2Cx, ENABLE);

	return 0;
}

uint8_t I2C::read(uint8_t devAddr, uint8_t regAddr) {

	uint32_t timeout;

	/*!< While the bus is busy */
	timeout = LONG_TIMEOUT;
	while (I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY )) {
		if ((timeout--) == 0)
			return 250;
	}

	/*!< Send START condition */
	I2C_GenerateSTART(I2Cx, ENABLE);

	/*!< Test on EV5 and clear it (cleared by reading SR1 then writing to DR) */
	timeout = FLAG_TIMEOUT;
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT )) {
		if ((timeout--) == 0)
			return 251;
	}

	/*!< Send devAddr for write */
	I2C_Send7bitAddress(I2Cx, devAddr, I2C_Direction_Transmitter );

	/*!< Test on EV6 and clear it */
	timeout = FLAG_TIMEOUT;
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED )) {
		if ((timeout--) == 0)
			return 252;
	}

	/*!< Send the regAddr to read from: Only one byte address */
	I2C_SendData(I2Cx, regAddr);

	/*!< Test on EV8 and clear it */
	timeout = FLAG_TIMEOUT;
	while (I2C_GetFlagStatus(I2Cx, I2C_FLAG_BTF ) == RESET) {
		if ((timeout--) == 0)
			return 253;
	}

	/*!< If device uses SCCB instead of I2C */
	if (ID == I2C_ID1) {
		/* Prepare Stop after receiving data */
		I2C_GenerateSTOP(I2Cx, ENABLE);
	}

	/*!< Send STRAT condition a second time */
	I2C_GenerateSTART(I2Cx, ENABLE);

	/*!< Test on EV5 and clear it (cleared by reading SR1 then writing to DR) */
	timeout = FLAG_TIMEOUT;
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT )) {
		if ((timeout--) == 0)
			return 254;
	}

	/*!< Send address for read */
	I2C_Send7bitAddress(I2Cx, devAddr, I2C_Direction_Receiver );

	timeout = FLAG_TIMEOUT;
	while (I2C_GetFlagStatus(I2Cx, I2C_FLAG_ADDR ) == RESET) {
		if ((timeout--) == 0)
			return 255;
	}

	/* Prepare an NACK for the next data received */
	I2C_AcknowledgeConfig(I2Cx, DISABLE);

	/* Test on I2C1 EV7 and clear it */
	timeout = FLAG_TIMEOUT; /* Initialize timeout value */
	while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED )) {
		/* If the timeout delay is exeeded, exit with error code */
		if ((timeout--) == 0)
			return 249;
	}

	/* Prepare Stop after receiving data */
	I2C_GenerateSTOP(I2Cx, ENABLE);

	/* Receive the Data */
	uint8_t data = I2C_ReceiveData(I2Cx);

	/* If all operations OK, return sEE_OK (0) */
	return data;
}
